<?php

namespace app\common\webdav\response;

use think\Response as ThinkResponse;

class WebdavStream extends ThinkResponse
{

    /**
     * 发送数据到客户端
     * @access public
     * @return void
     * @throws \InvalidArgumentException
     */
    public function send(): void
    {
        // 处理输出数据
        $body = $this->getData();

        if (!headers_sent()) {
            if (!empty($this->header)) {
                // 发送状态码
                http_response_code($this->code);
                // 发送头部信息
                foreach ($this->header as $name => $val) {
                    header($name . (!is_null($val) ? ':' . $val : ''));
                }
            }

            if ($this->cookie) {
                $this->cookie->save();
            }
        }

        if (null === $body) {
            return;
        }

        if (is_callable($body)) {
            $body();

            return;
        }

        $contentLength = $this->getHeader('Content-Length');
        if (null !== $contentLength) {
            $output = fopen('php://output', 'wb');
            if (is_resource($body) && 'stream' == get_resource_type($body)) {
                if (PHP_INT_SIZE > 4) {
                    // use the dedicated function on 64 Bit systems
                    // a workaround to make PHP more possible to use mmap based copy, see https://github.com/sabre-io/http/pull/119
                    $left = (int) $contentLength;
                    // copy with 4MiB chunks
                    $chunk_size = 4 * 1024 * 1024;
                    stream_set_chunk_size($output, $chunk_size);
                    // If this is a partial response, flush the beginning bytes until the first position that is a multiple of the page size.
                    $contentRange = $this->getHeader('Content-Range');
                    // Matching "Content-Range: bytes 1234-5678/7890"
                    if (null !== $contentRange && preg_match('/^bytes\s([0-9]+)-([0-9]+)\//i', $contentRange, $matches)) {
                        // 4kB should be the default page size on most architectures
                        $pageSize = 4096;
                        $offset = (int) $matches[1];
                        $delta = ($offset % $pageSize) > 0 ? ($pageSize - $offset % $pageSize) : 0;
                        if ($delta > 0) {
                            $left -= stream_copy_to_stream($body, $output, min($delta, $left));
                        }
                    }
                    while ($left > 0) {
                        $copied = stream_copy_to_stream($body, $output, min($left, $chunk_size));
                        // stream_copy_to_stream($src, $dest, $maxLength) must return the number of bytes copied or false in case of failure
                        // But when the $maxLength is greater than the total number of bytes remaining in the stream,
                        // It returns the negative number of bytes copied
                        // So break the loop in such cases.
                        if ($copied <= 0) {
                            break;
                        }
                        $left -= $copied;
                    }
                } else {
                    // workaround for 32 Bit systems to avoid stream_copy_to_stream
                    while (!feof($body)) {
                        fwrite($output, fread($body, 8192));
                    }
                }
            } else {
                fwrite($output, $body, (int) $contentLength);
            }
        } else {
            file_put_contents('php://output', $body);
        }

        if (is_resource($body)) {
            fclose($body);
        }

        if (function_exists('fastcgi_finish_request')) {
            // 提高页面响应
            fastcgi_finish_request();
        }
    }
}
